package de.mmis.core.tuplespace;

import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.LeafNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;

/**
 * Provides read access to all command histories of each device.
 * 
 * @author Martin Nyolt
 * 
 */
public class TuplespaceCommandHistory {

	private final static Logger LOGGER = Logger
			.getLogger(TuplespaceCommandHistory.class);

	public static class Command {
		private final Tree id;
		private final Tree command;
		private final long time;

		public Command(Tree id, Tree command, long time) {
			this.id = id;
			this.command = command;
			this.time = time;
		}

		/**
		 * @return the id
		 */
		public Tree getId() {
			return id;
		}

		/**
		 * @return the command
		 */
		public Tree getCommand() {
			return command;
		}

		/**
		 * @return the time
		 */
		public long getTime() {
			return time;
		}
	}

	private final Tuplespace space;

	public TuplespaceCommandHistory(Tuplespace space) {
		this.space = space;
	}

	public List<Command> getHistory(Tree id) throws TuplespaceException {
		if (id == null)
			id = new LeafNode("?");

		InnerNode pattern = new InnerNode(new LeafNode(
				":command-history-queue-elem"), id, new LeafNode("?"),
				new LeafNode("?"));

		List<Tree> list = space.scan(pattern);

		Command[] commands = new Command[list.size()];
		int i = 0;
		for (Tree e : list) {
			InnerNode exp = (InnerNode) e;
			Tree id_ = exp.getSubTree(1);
			Tree entry = exp.getSubTree(3);
			if (!(entry instanceof InnerNode)) {
				LOGGER.warn("Warning: " + entry + " is not a Struct",
						new Exception("Warning: " + entry + " is not a Struct"));
				continue;
			}

			InnerNode en = (InnerNode) entry;
			long time;

			try {
				time = en.getSubTree(1).getAsLong();
			} catch (DeserializableException e1) {
				LOGGER.warn(e1, e1);
				continue;
			}

			Command c = new Command(id_, en.getSubTree(0), time);
			commands[i++] = c;
		}

		Arrays.sort(commands, new Comparator<Command>() {
			@Override
			public int compare(Command o1, Command o2) {
				if (o1 != null && o2 != null) {
					if (o1.time > o2.time)
						return -1;
					if (o1.time < o2.time)
						return 1;
					return 0;
				}

				if (o1 == null && o2 != null)
					return 1;

				if (o1 != null && o2 == null)
					return -1;

				return 0;
			}
		});

		LinkedList<Command> result = new LinkedList<TuplespaceCommandHistory.Command>();

		for (Command c : commands) {
			if (c != null)
				result.add(c);
		}

		return result;
	}
}
